
#include "env-sample-v3.h"

namespace env_sample_v3
{
	class EnvSampleVars : public SessionSampleVars
	{
	public:
		SPADAS_VARS(EnvSample, SessionSampleVars)
		EnvSampleData data;

		Bool supportInterpolation() override
		{
			return TRUE;
		}

		SessionGeneralSample toGeneralSample()
		{
			SessionGeneralSample sample;
			sample.timestamp() = timestamp;

			Array<GeneralElement> values(64 + data.spots.size() * 2);
			sample.values() = values;

			values[1] = data.spots.size();

			values[2] = (UInt)data.weatherType;
			values[3] = data.temperature;
			values[4] = data.pm25;
			values[5] = data.speedLimit.valid ? data.speedLimit.value : OptionalDouble();
			values[6] = (UInt)data.roadType;
			values[7] = data.illumination;
			values[8] = data.windSpeed;
			values[9] = (UInt)data.roadTraffic;
			values[10] = data.roadName;
			values[11] = data.pressure;
			values[12] = data.humidity;
			values[13] = data.visibility;
			values[14] = (UInt)data.windDirection;
			values[15] = data.pm10;
			values[16] = (UInt)data.roadSurfaceStatus;
			values[17] = (UInt)data.currentLaneTraffic;
			values[18] = (UInt)data.leftLaneTraffic;
			values[19] = (UInt)data.rightLaneTraffic;
			values[20] = (UInt)data.typhoonStatus;
			values[21] = (UInt)data.tornadoStatus;
			values[22] = (UInt)data.lightningStatus;
			values[23] = (UInt)data.hailStatus;
			values[24] = data.rainfall;
			values[25] = data.cumulativeRainfall;
			values[26] = data.accurateWindDirection;
			values[27] = data.totalRadiationIntensity;
			values[28] = data.ultravioletRadiationIntensity;
			values[29] = data.o3Concentration;
			values[30] = data.coConcentration;
			values[31] = data.so2Concentration;
			values[32] = data.no2Concentration;

			for (UInt i = 0; i < data.spots.size(); i++)
			{
				values[64 + 2 * i] = (UInt)data.spots[i].spotType;
				values[64 + 2 * i + 1] = data.spots[i].distance;
			}

			sample.significantCount() = values.size();
			return sample;
		}

		Bool fromSample(String protocol, SessionSample sample) override
		{
			if (!sample.is<SessionGeneralSample>()) return FALSE;

			auto generalSample = sample.as<SessionGeneralSample>();
			timestamp = generalSample.timestamp();
			auto values = generalSample.values();

			if (protocol == "env-sample-v3")
			{
				if (values.size() < 2) return FALSE;

				UInt roadNameBinSize = (UInt)values[0].value;
				UInt spotsCount = (UInt)values[1].value;
				if (values.size() != 64 + spotsCount * 2 + roadNameBinSize) return FALSE;

				data.weatherType = values[2].valid ? (WeatherType)(Int)values[2].value : WeatherType::Unknown;
				data.temperature = values[3];
				data.pm25 = values[4];
				data.speedLimit = values[5].valid ? (Int)values[5].value : OptionalInt();
				data.roadType = values[6].valid ? (RoadType)(Int)values[6].value : RoadType::Unknown;
				data.illumination = values[7];
				data.windSpeed = values[8];
				data.roadTraffic = values[9].valid ? (TrafficStatus)(Int)values[9].value : TrafficStatus::Unknown;
				data.pressure = values[11];
				data.humidity = values[12];
				data.visibility = values[13];
				data.windDirection = values[14].valid ? (WindDirection)(Int)values[14].value : WindDirection::Unknown;
				data.pm10 = values[15];
				data.roadSurfaceStatus = values[16].valid ? (RoadSurfaceStatus)(Int)values[16].value : RoadSurfaceStatus::Unknown;
				data.currentLaneTraffic = values[17].valid ? (TrafficStatus)(Int)values[17].value : TrafficStatus::Unknown;
				data.leftLaneTraffic = values[18].valid ? (TrafficStatus)(Int)values[18].value : TrafficStatus::Unknown;
				data.rightLaneTraffic = values[19].valid ? (TrafficStatus)(Int)values[19].value : TrafficStatus::Unknown;
				data.typhoonStatus = values[20].valid ? (DisasterStatus)(Int)values[20].value : DisasterStatus::Unknown;
				data.tornadoStatus = values[21].valid ? (DisasterStatus)(Int)values[21].value : DisasterStatus::Unknown;
				data.lightningStatus = values[22].valid ? (DisasterStatus)(Int)values[22].value : DisasterStatus::Unknown;
				data.hailStatus = values[23].valid ? (DisasterStatus)(Int)values[23].value : DisasterStatus::Unknown;
				data.rainfall = values[24];
				data.cumulativeRainfall = values[25];
				data.accurateWindDirection = values[26];
				data.totalRadiationIntensity = values[27];
				data.ultravioletRadiationIntensity = values[28];
				data.o3Concentration = values[29];
				data.coConcentration = values[30];
				data.so2Concentration = values[31];
				data.no2Concentration = values[32];

				data.spots = Array<Spot>(spotsCount);
				for (UInt i = 0; i < data.spots.size(); i++)
				{
					data.spots[i].spotType = (SpotType)(Int)values[64 + 2 * i].value;
					data.spots[i].distance = values[64 + 2 * i + 1].value;
				}

				if (roadNameBinSize > 0)
				{
					Binary roadNameBin(roadNameBinSize);
					for (UInt i = 0; i < roadNameBinSize; i++)
					{
						roadNameBin[i] = (Byte)values[64 + data.spots.size() * 2 + i].value;
					}
					data.roadName = roadNameBin;
				}
				else data.roadName = String();

				if (data.roadName.isEmpty() && values[10].valid && values[10].isText) data.roadName = values[10].text;

				return TRUE;
			}
			else if (protocol == "env-sample-v2")
			{
				if (values.size() < 2) return FALSE;

				UInt roadNameBinSize = (UInt)values[0].value;
				UInt spotsCount = (UInt)values[1].value;
				if (values.size() != 10 + spotsCount * 2 + roadNameBinSize) return FALSE;

				data.weatherType = values[2].valid ? (WeatherType)(Int)values[2].value : WeatherType::Unknown;
				data.temperature = values[3];
				data.pm25 = values[4];
				data.speedLimit = values[5].valid ? (Int)values[5].value : OptionalInt();
				data.roadType = values[6].valid ? (RoadType)(Int)values[6].value : RoadType::Unknown;
				data.illumination = values[7];
				data.windSpeed = values[8];
				data.roadTraffic = values[9].valid ? (TrafficStatus)(Int)values[9].value : TrafficStatus::Unknown;

				data.spots = Array<Spot>(spotsCount);
				for (UInt i = 0; i < data.spots.size(); i++)
				{
					data.spots[i].spotType = (SpotType)(Int)values[10 + 2 * i].value;
					data.spots[i].distance = values[10 + 2 * i + 1].value;
				}

				if (roadNameBinSize > 0)
				{
					Binary roadNameBin(roadNameBinSize);
					for (UInt i = 0; i < roadNameBinSize; i++)
					{
						roadNameBin[i] = (Byte)values[10 + data.spots.size() * 2 + i].value;
					}
					data.roadName = roadNameBin;
				}
				else data.roadName = String();

				return TRUE;
			}

			return FALSE;
		}

		SessionSample interpolate(SessionSample& ss1, Double w1, SessionSample& ss2, Double w2, FullTimestamp timestamp) override
		{
			if (!ss1.is<EnvSample>() || !ss2.is<EnvSample>()) return SessionSample();

			EnvSampleData& s1 = ss1.as<EnvSample>().getVars()->data;
			EnvSampleData& s2 = ss2.as<EnvSample>().getVars()->data;

			EnvSample es;
			es.timestamp() = timestamp;

			EnvSampleData& so = es.getVars()->data;
			so.weatherType = w1 > w2 ? s1.weatherType : s2.weatherType;
			so.temperature = s1.temperature * w1 + s2.temperature * w2;
			so.pm25 = s1.pm25 * w1 + s2.pm25 * w2;
			so.speedLimit = w1 > w2 ? s1.speedLimit : s2.speedLimit;
			so.roadType = w1 > w2 ? s1.roadType : s2.roadType;
			so.illumination = s1.illumination * w1 + s2.illumination * w2;
			so.windSpeed = s1.windSpeed * w1 + s2.windSpeed * w2;
			so.roadTraffic = w1 > w2 ? s1.roadTraffic : s2.roadTraffic;
			so.roadName = w1 > w2 ? s1.roadName : s2.roadName;
			so.pressure = s1.pressure * w1 + s2.pressure * w2;
			so.humidity = s1.humidity * w1 + s2.humidity * w2;
			so.visibility = s1.visibility * w1 + s2.visibility * w2;
			so.windDirection = w1 > w2 ? s1.windDirection : s2.windDirection;
			so.pm10 = s1.pm10 * w1 + s2.pm10 * w2;
			so.roadSurfaceStatus = w1 > w2 ? s1.roadSurfaceStatus : s2.roadSurfaceStatus;
			so.currentLaneTraffic = w1 > w2 ? s1.currentLaneTraffic : s2.currentLaneTraffic;
			so.leftLaneTraffic = w1 > w2 ? s1.leftLaneTraffic : s2.leftLaneTraffic;
			so.rightLaneTraffic = w1 > w2 ? s1.rightLaneTraffic : s2.rightLaneTraffic;
			so.typhoonStatus = w1 > w2 ? s1.typhoonStatus : s2.typhoonStatus;
			so.tornadoStatus = w1 > w2 ? s1.tornadoStatus : s2.tornadoStatus;
			so.lightningStatus = w1 > w2 ? s1.lightningStatus : s2.lightningStatus;
			so.hailStatus = w1 > w2 ? s1.hailStatus : s2.hailStatus;
			so.rainfall = s1.rainfall * w1 + s2.rainfall * w2;
			so.cumulativeRainfall = s1.cumulativeRainfall * w1 + s2.cumulativeRainfall * w2;
			so.accurateWindDirection = s1.accurateWindDirection * w1 + s2.accurateWindDirection * w2;
			so.totalRadiationIntensity = s1.totalRadiationIntensity * w1 + s2.totalRadiationIntensity * w2;
			so.ultravioletRadiationIntensity = s1.ultravioletRadiationIntensity * w1 + s2.ultravioletRadiationIntensity * w2;
			so.o3Concentration = s1.o3Concentration * w1 + s2.o3Concentration * w2;
			so.coConcentration = s1.coConcentration * w1 + s2.coConcentration * w2;
			so.so2Concentration = s1.so2Concentration * w1 + s2.so2Concentration * w2;
			so.no2Concentration = s1.no2Concentration * w1 + s2.no2Concentration * w2;
			so.spots = w1 > w2 ? s1.spots : s2.spots;

			return es.as<SessionSample>();
		}
	};
}

using namespace env_sample_v3;

EnvSample::EnvSample() : Object<EnvSampleVars>(new EnvSampleVars, TRUE)
{}

FullTimestamp& EnvSample::timestamp()
{
	return vars->timestamp;
}

EnvSampleData *EnvSample::operator ->()
{
    return &vars->data;
}

String EnvSample::getProtocolName()
{
    return "env-sample-v3";
}

SessionGeneralSample EnvSample::toGeneralSample()
{
    return vars->toGeneralSample();
}

Array<String> EnvSample::getProtocolNames()
{
	return Array<String>::create(2, "env-sample-v2", "env-sample-v3");
}

Bool EnvSample::fromGeneralSample(String protocol, SessionGeneralSample generalSample)
{
    return vars->fromSample(protocol, generalSample.as<SessionSample>());
}